package cn.iocoder.yudao.module.digital.service.cardchannel.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.digital.api.cardkeymanager.dto.CardKeySecretDTO;
import cn.iocoder.yudao.module.digital.controller.admin.cardkeysecret.vo.CardkeySecretSaveReqVO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyorder.CardkeyOrderDO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyproduct.CardkeyProductDO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyqueuein.CardkeyQueueInDO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyqueueout.CardkeyQueueOutDO;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeysecret.CardkeySecretDO;
import cn.iocoder.yudao.module.digital.dal.redis.cardkeymanager.CardKeyManagerLockRedisDAO;
import cn.iocoder.yudao.module.digital.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.digital.enums.cardkeyqueue.CardkeyInQueueStatusEnum;
import cn.iocoder.yudao.module.digital.enums.cardkeyqueue.CardkeyOutQueueStatusEnum;
import cn.iocoder.yudao.module.digital.enums.cardkeysecret.CardKeySourceEnum;
import cn.iocoder.yudao.module.digital.enums.cardkeysecret.ClaimStatusEnum;
import cn.iocoder.yudao.module.digital.framework.cardkey.config.CardKeyConfig;
import cn.iocoder.yudao.module.digital.service.cardchannel.ICardChannelManager;
import cn.iocoder.yudao.module.digital.service.cardchannel.ICardSecretManager;
import cn.iocoder.yudao.module.digital.service.cardchannel.model.common.*;
import cn.iocoder.yudao.module.digital.service.cardkeyorder.CardkeyOrderService;
import cn.iocoder.yudao.module.digital.service.cardkeyproduct.CardkeyProductService;
import cn.iocoder.yudao.module.digital.service.cardkeyqueuein.CardkeyQueueInService;
import cn.iocoder.yudao.module.digital.service.cardkeyqueueout.CardkeyQueueOutService;
import cn.iocoder.yudao.module.digital.service.cardkeysecret.CardkeySecretService;
import cn.iocoder.yudao.module.infra.util.TraceIdUtil;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderItemRespDTO;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderDeliveryStatusEnum;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 必须保证：
 * 1.先订单锁，再商品锁
 * 2.入库队列超时的时候，该订单对应的出库队列肯定已经超时了，可以通过设置入库队列超时时间 < 出库队列超时时间实现
 * 3.第1个用户订单补充库存失败了，此时，用户提交了第2个订单然后触发补库存，此时补的库存不能加上第1个订单缺少的数量，因为如果咱们程序有bug会导致咱们在礼品卡厂商的预付款很快耗尽
 * <p>
 * 用户下第1个订单
 * 需要补充的库存 = （新下单的数量 + 等待出库的数量）- （已有的库存量 + 正在补充的库存量）
 * 1 = （1 + 0） - （0 + 0）
 * -------
 * <p>
 * 此时补充库存失败，但是还未到入库超时时间，先设置入库队列状态为“将要失败”，等到超时后再修改为“最终失败”
 * <p>
 * -------
 * <p>
 * 用户下第2个订单
 * 1 = （1 + 1） - （0 + 1[将要失败，还未最终失败的]）
 */
@Service
@Slf4j
public class CardSecretManagerImpl implements ICardSecretManager {
    private final String productNoManualDelivery = "ManualDelivery"; // 手动发货商品编号

    @Resource
    private CardKeyManagerLockRedisDAO lockRedisDAO;
    @Resource
    private TradeOrderApi orderApi;
    @Resource
    private CardkeySecretService cardkeySecretService;
    @Resource
    private ProductSkuApi skuApi;
    @Resource
    private ProductSpuApi spuApi;
    @Resource
    private CardkeyProductService cardkeyProductService;
    @Resource
    private CardkeyQueueInService inQueueService;
    @Resource
    private CardkeyQueueOutService outQueueService;
    @Resource
    private ICardChannelManager cdkChannelManager;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private CardKeyConfig cardKeyConfig;
    @Resource
    private CardkeyOrderService cardkeyOrderService;

    @PostConstruct
    public void init() {
        log.info("cardKeyConfig配置: {}", cardKeyConfig);
    }

    @Override
    public void pollTimeoutUnSuccessfulOrders() {
        log.info("重查超时订单-开始");
        final List<CardkeyOrderDO> orders = cardkeyOrderService.getTimeoutCardKeyOrder(Duration.ofMinutes(60));
        log.info("超时订单数量, orders.size = {}", orders.size());

        for (CardkeyOrderDO cardKeyOrder : orders) {
            final CardKeySourceEnum source = CardKeySourceEnum.fromValue(cardKeyOrder.getSource());
            final String cardOrderId = cardKeyOrder.getId();
            final CdkGenCardResult cardResult = cdkChannelManager.onlyQueryOrderResult(cardKeyOrder);
            if (cardResult == null) {
                continue;
            }

            // 是否订单完成并且有成功的卡密
            final CdkOrderStatusEnum orderStatus = cardResult.getOrderStatus();
            final boolean isCreateCardCompleted = orderStatus.isCreateCardCompletedStatus();
            if (!isCreateCardCompleted) {
                log.warn("卡密订单未完成, cardOrderId = {}, orderStatus = {}, createDuration = {}, cardResult = {}",
                        cardOrderId, orderStatus, cardResult.getCreateDuration(), JsonUtils.toJsonString(cardResult));
                continue;
            }

            // 卡密订单已完成（可能成功或失败）
            log.info("卡密订单已完成, cardOrderId = {}, orderStatus = {}, createDuration = {}, cardResult = {}",
                    cardOrderId, orderStatus, cardResult.getCreateDuration(), JsonUtils.toJsonString(cardResult));

            if (CollUtil.isEmpty(cardResult.getCards())) {
                // 更新卡密订单状态
                cdkChannelManager.updateCardKeyOrder(cardKeyOrder, cardResult);
                continue;
            }

            final Set<String> productLockKeys = new HashSet<>();
            for (CdkCardSecret card : cardResult.getCards()) {
                // 保存卡密
                final String productKey = lockRedisDAO.getProductKey(source, card.getProductNo(), card.getBuyDenomination());
                productLockKeys.add(productKey);
            }
            log.info("卡密订单已完成-获取卡密商品锁-开始, source = {}, cardOrderId = {}, productLockKeys = {}",
                    source, cardOrderId, productLockKeys);

            lockRedisDAO.lockProducts(productLockKeys, () -> {
                log.info("卡密订单已完成-获取卡密商品锁-成功, source = {}, cardOrderId = {}, productLockKeys = {}",
                        source, cardOrderId, productLockKeys);

                transactionTemplate.execute(t -> {
                    for (CdkCardSecret card : cardResult.getCards()) {
                        // 保存卡密
                        saveCards(cardResult, cardOrderId, source, card);
                    }

                    // 更新卡密订单状态
                    cdkChannelManager.updateCardKeyOrder(cardKeyOrder, cardResult);
                    return null;
                });
                return null;
            });
        }

        log.info("重查超时订单-结束");
    }


    /**
     * 通过客服领取卡密
     *
     * @param orderNo 订单流水号
     * @param secret  密钥
     */
    @Override
    public List<CardKeySecretDTO> claimCardKeySecretByCustomerService(String orderNo, String secret) {
        log.info("通过客服领取卡密-开始, orderNo = {}, secret = {}", orderNo, secret);
        final List<CardKeySecretDTO> cardKeySecretList = lockRedisDAO.lockOrder(orderNo, () -> {
            log.info("通过客服领取卡密-获取到订单锁, orderNo = {}", orderNo);

            // 查询订单发货状态
            final TradeOrderRespDTO order = orderApi.getOrderByNo(orderNo);
            log.info("通过客服领取卡密-查询订单发货状态, orderNo = {}, deliveryStatus = {}", orderNo, order.getDeliveryStatus());

            if (!order.getPayStatus()) {
                throw new IllegalStateException(String.format("订单未支付 [%s]", orderNo));
            }

            if (TradeOrderDeliveryStatusEnum.hasDelivered(order.getDeliveryStatus())) {
                log.warn("通过客服领取卡密-订单已发货, orderNo = {}", orderNo);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, "订单已发货，请勿重复发货");
            }

            // 存储人工填写的密钥 & 更新订单发货状态
            final List<CardKeySecretDTO> secretDTOList = createCardKeySecretByManualAndUpdateOrderStatus(secret, order);
            return secretDTOList;
        });
        return cardKeySecretList;
    }

    // 存储人工填写的密钥 & 更新订单发货状态
    private List<CardKeySecretDTO> createCardKeySecretByManualAndUpdateOrderStatus(String secret, TradeOrderRespDTO order) {
        Assert.isTrue(order.getItems().size() == 1);
        final TradeOrderItemRespDTO orderItem = order.getItems().get(0);
        final Long skuId = orderItem.getSkuId();
        final int buyCount = orderItem.getCount();
        Integer orderDenomination = null;
        if (orderItem.getCustomDenomination() != null && orderItem.getCustomDenomination()) {
            // 自定义面额
            orderDenomination = orderItem.getNominalValue();
        }

        // 解析卡密
        final List<CardKeySecretDTO> secretDTOList = parseCardSecretData(secret);
        if (buyCount != secretDTOList.size()) {
            log.info("填入的卡密数量跟用户购买的数量不一致, orderNo = {}, secretDTOList.size = {}, buyCount = {}, secretDTOList = {}",
                    order.getNo(), secretDTOList.size(), buyCount, JsonUtils.toJsonString(secretDTOList));
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, String.format("填入的卡密数量(%s)跟用户购买的数量(%s)不一致", secretDTOList.size(), buyCount));
        }

        // 创建保存卡密的 vo
        final List<CardkeySecretSaveReqVO> secretVoList = createCardKeySecretByManual(order, skuId, orderDenomination, secretDTOList);

        // 提交事务
        transactionTemplate.execute(v -> {
            // 存储人工填写的密钥
            secretVoList.forEach(vo -> {
                cardkeySecretService.createCardkeySecret(vo);
            });
            log.info("通过客服领取卡密-成功, orderNo = {}, secretDTOList = {}", order.getNo(), JsonUtils.toJsonString(secretDTOList));

            // 更新订单发货状态
            this.updateOrderDeliveryStatus(order.getNo(), true, null, null);

            return null;
        });

        return secretDTOList;
    }

    // 存储人工填写的密钥
    private List<CardkeySecretSaveReqVO> createCardKeySecretByManual(TradeOrderRespDTO order, Long skuId, Integer orderDenomination, List<CardKeySecretDTO> secretDTOList) {
        final Long userId = order.getUserId();
        final String orderNo = order.getNo();

        // 查询sku
        final ProductSkuRespDTO sku = skuApi.getSku(skuId);
        final String productNo = sku.getCardKeyProductNo();

        // 查询spu
        final ProductSpuRespDTO spu = spuApi.getSpu(sku.getSpuId());

        // 商品的货币
        final String currencyCode = spu.getCurrencyCode();

        // 面额
        final Integer buyDenomination;
        if (StrUtil.isNotEmpty(productNo)) {
            // 查询卡密商品
            final CardkeyProductDO cardKeyProduct = cardkeyProductService.getCardKeyProductByNo(productNo);
            log.info("卡密发货预处理-查询卡密商品, productNo = {}", productNo);
            buyDenomination = getBuyDenomination(userId, orderNo, orderDenomination, cardKeyProduct, sku);
        } else {
            if (sku.getCustomDenomination()) {
                // sku自定义面额，使用用户传入的
                buyDenomination = orderDenomination;
            } else {
                // sku固定面额
                buyDenomination = sku.getDenominationFixed();
            }
        }
        log.info("获取面额, orderNo = {}, skuId = {}, productNo = {}, buyDenomination = {}",
                orderNo, sku.getId(), productNo, buyDenomination);

        final List<CardkeySecretSaveReqVO> voList = new ArrayList<>();
        for (CardKeySecretDTO secretDTO : secretDTOList) {
            final CardkeySecretDO existedSecret = cardkeySecretService.getCardKeySecretByNumberCodePin(
                    ClaimStatusEnum.YES, secretDTO.getCardNumber(), secretDTO.getCode(), secretDTO.getPin());
            if (existedSecret != null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR,
                        String.format("该卡密已经被使用, cardNumber = %s, code = %s, pin = %s",
                                secretDTO.getCardNumber(), secretDTO.getCode(), secretDTO.getPin()));
            }

            final CardkeySecretSaveReqVO vo = new CardkeySecretSaveReqVO();
            vo.setEGiftId(IdUtil.objectId());
            vo.setSource(CardKeySourceEnum.MANUAL.getValue());
            vo.setProductNo(StrUtil.isNotEmpty(productNo) ? productNo : productNoManualDelivery);
            vo.setClaimStatus(ClaimStatusEnum.YES.getValue());
            vo.setClaimDate(LocalDateTime.now());
            vo.setDenomination(buyDenomination);
            vo.setCurrencyCode(currencyCode);
            final String requestId = IdUtil.fastSimpleUUID();
            vo.setRequestId(requestId);
            vo.setOrderNo(orderNo);
            vo.setUserId(userId);
            vo.setCardNumber(secretDTO.getCardNumber());
            vo.setCode(secretDTO.getCode());
            vo.setPin(secretDTO.getPin());

            voList.add(vo);
        }
        return voList;
    }

    @Override
    public List<CardKeySecretDTO> parseCardSecretData(String textData) {
        String[] lines = textData.trim().split("\\n");

        List<CardKeySecretDTO> cardKeySecrets = new ArrayList<>();

        // Iterate through each line and parse the data into CardKeySecretDTO objects
        for (String line : lines) {
            String[] parts = line.split("->");
            if (parts.length == 3) {
                CardKeySecretDTO cardKeySecret = new CardKeySecretDTO(getSecretFieldValue(parts[0]), getSecretFieldValue(parts[1]), getSecretFieldValue(parts[2]));
                cardKeySecrets.add(cardKeySecret);
            } else {
                throw new RuntimeException("Invalid data format: " + line);
            }
        }

        return cardKeySecrets;
    }

    private String getSecretFieldValue(String value) {
        if ("null".equalsIgnoreCase(value)) {
            return null;
        }
        return value;
    }

    @Override
    public void preprocessCardKeyShipping(String orderNo, Long skuId, int buyCount, Integer orderDenomination) {
        try {
            TraceIdUtil.setTraceId(IdUtil.fastSimpleUUID());
            log.info("卡密发货预处理-开始, orderNo = {}, skuId = {}, buyCount = {}, orderDenomination = {}",
                    orderNo, skuId, buyCount, orderDenomination);

            lockRedisDAO.lockOrder(orderNo, () -> {
                log.info("卡密发货预处理-获取到订单锁, orderNo = {}", orderNo);

                // 查询订单发货状态
                final TradeOrderRespDTO order = orderApi.getOrderByNo(orderNo);
                log.info("卡密发货预处理-查询订单发货状态, orderNo = {}, deliveryStatus = {}", orderNo, order.getDeliveryStatus());

                if (!order.getPayStatus()) {
                    log.error("卡密发货预处理-订单未支付, orderNo = {}", orderNo);
                    throw new IllegalStateException(String.format("订单未支付 [%s]", orderNo));
                }

                final boolean hasDelivered = TradeOrderDeliveryStatusEnum.hasDelivered(order.getDeliveryStatus());
                if (hasDelivered) {
                    // 已发货，查询已发货的卡密
                    final List<String> secretIdList = getCardSecretIds(orderNo);
                    log.warn("卡密发货预处理-订单已发货, orderNo = {}, secretIdList = {}", orderNo, secretIdList);
                    return null;
                }

                // 卡密发货预处理
                preprocessCardKeyShipping0(order, skuId, buyCount, orderDenomination);

                return null;
            });
        } finally {
            TraceIdUtil.removeTraceId();
        }
    }

    private void preprocessCardKeyShipping0(TradeOrderRespDTO order, Long skuId, int buyCount, Integer orderDenomination) {
        final Long userId = order.getUserId();
        final String orderNo = order.getNo();

        try {
            // 查询sku
            final ProductSkuRespDTO sku = skuApi.getSku(skuId);
            final String productNo = sku.getCardKeyProductNo();

            // 查询卡密商品
            final CardkeyProductDO cardKeyProduct = cardkeyProductService.getCardKeyProductByNo(productNo);
            log.info("卡密发货预处理-查询卡密商品, productNo = {}", productNo);

            // 获取面额
            final Integer buyDenomination = getBuyDenomination(userId, orderNo, orderDenomination, cardKeyProduct, sku);

            log.info("卡密发货预处理-获取卡密商品锁-开始, userId = {}, orderNo = {}, productNo = {}", userId, orderNo, productNo);
            final CardKeySourceEnum source = CardKeySourceEnum.fromValue(cardKeyProduct.getSource());
            lockRedisDAO.lockProduct(source, productNo, buyDenomination, () -> {
                log.info("卡密发货预处理-获取卡密商品锁-成功, userId = {}, orderNo = {}, cardKeyProductNo = {}", userId, orderNo, productNo);
                preprocessCardKeyShipping1(orderNo, userId, buyCount, productNo, buyDenomination, cardKeyProduct);
                return null;
            });
        } catch (Exception e) {
            log.error(String.format("卡密发货预处理-异常, userId = %s, orderNo = %s", userId, orderNo), e);
            updateOrderDeliveryStatus(orderNo, false, e.getMessage(), null);
        }
    }

    private void preprocessCardKeyShipping1(String orderNo, Long userId, int buyCount,
                                            String productNo, Integer buyDenomination, CardkeyProductDO cardKeyProduct) {

        // 判断当前卡密商品来源
        final CardKeySourceEnum source = CardKeySourceEnum.fromValue(cardKeyProduct.getSource());
        final int outQueueTimeoutMinutes = cardKeyConfig.getChannelDifference().get(source.getKey()).getOutQueueTimeoutMinutes();

        if (source == CardKeySourceEnum.MANUAL) {
            log.info("卡密发货预处理-采购的商品, orderNo = {}, userId = {}, productNo = {}", orderNo, userId, productNo);
            // 采购的商品，直接写入 out队列，不写入 in队列，因为无法自动补货
            outQueueService.createCardKeyOutQueue(source, productNo, cardKeyProduct.getCurrencyCode(),
                    buyDenomination, orderNo, userId, buyCount, outQueueTimeoutMinutes);
            log.info("写入out队列, orderNo = {}, userId = {}, productNo = {}, buyDenomination = {}, buyCount = {}",
                    orderNo, userId, productNo, buyDenomination, buyCount);
            return;
        }

        // 查询已有的库存量
        final long existingStock = cardkeySecretService.countCardKeySecretByProductNo(ClaimStatusEnum.NO, productNo, buyDenomination);
        log.info("查询已有的库存量, productNo = {}, buyDenomination = {}, existingStock = {}", productNo, buyDenomination, existingStock);

        // 查询正在补充的库存量
        final long replenishingStock = inQueueService.countQueue(
                Arrays.asList(CardkeyInQueueStatusEnum.INIT, CardkeyInQueueStatusEnum.IN_PROGRESS, CardkeyInQueueStatusEnum.WILL_FAIL), productNo, buyDenomination);
        log.info("查询正在补充的库存量, productNo = {}, buyDenomination = {}, replenishingStock = {}", productNo, buyDenomination, replenishingStock);

        // 查询等待出库的数量
        final long waitingInventoryCount = outQueueService.countQueue(
                Arrays.asList(CardkeyOutQueueStatusEnum.INIT, CardkeyOutQueueStatusEnum.IN_PROGRESS), productNo, buyDenomination);
        log.info("查询等待出库的数量, productNo = {}, buyDenomination = {}, waitingInventoryCount = {}", productNo, buyDenomination, waitingInventoryCount);

        // 当前需要补充的库存量
        final long neededReplenishStock = (buyCount + waitingInventoryCount) - (existingStock + replenishingStock);
        log.info("当前需要补充的库存量, productNo = {}, buyDenomination = {}, neededReplenishStock = {}", productNo, buyDenomination, neededReplenishStock);

        // 提交事务
        transactionTemplate.execute(status -> {
            if (neededReplenishStock > 0) {
                inQueueService.createCardKeyInQueue(orderNo, userId, source, productNo, cardKeyProduct.getCurrencyCode(),
                        buyDenomination, (int) neededReplenishStock);
                log.info("写入in队列, orderNo = {}, userId = {}, productNo = {}, buyDenomination = {}, neededReplenishStock = {}",
                        orderNo, userId, productNo, buyDenomination, neededReplenishStock);
            }

            outQueueService.createCardKeyOutQueue(source, productNo, cardKeyProduct.getCurrencyCode(),
                    buyDenomination, orderNo, userId, buyCount, outQueueTimeoutMinutes);
            log.info("写入out队列, orderNo = {}, userId = {}, productNo = {}, buyDenomination = {}, buyCount = {}",
                    orderNo, userId, productNo, buyDenomination, buyCount);
            return null;
        });
    }

    @Override
    public void pollUnProcessingInQueue() {
        try {
            final String rootTraceId = IdUtil.fastSimpleUUID();
            TraceIdUtil.setTraceId(rootTraceId);
            log.info("轮训in队列-开始");

            // 处理未完成的队列
            handleUnProcessingInQueue();

            // 处理将要失败的in队列
            handleWillFailInQueues();

            log.info("轮训in队列-结束");
        } catch (Exception e) {
            log.error("轮训in队列-异常", e);
        } finally {
            TraceIdUtil.removeTraceId();
        }
    }

    // 处理将要失败的in队列
    private void handleWillFailInQueues() {
        log.info("处理将要失败的in队列-开始");

        // 查询将要失败的in队列
        final List<CardkeyQueueInDO> willFailQueueList = inQueueService.getCardKeyInQueueByStatus(Arrays.asList(CardkeyInQueueStatusEnum.WILL_FAIL));
        log.info("查询将要失败的in队列, willFailQueueListSize = {}", willFailQueueList.size());

        if (CollUtil.isNotEmpty(willFailQueueList)) {
            willFailQueueList.parallelStream().forEach(queue -> {
                // 等待超时后，将队列状态由“将要失败”修改为“最终失败”，之后用户新下订单的时候“正在补充的库存”就不会再计算在内了
                final Long inQueueTimeoutMinutes = cardKeyConfig.getGlobal().getInQueueTimeoutMinutes();
                final CardKeySourceEnum source = CardKeySourceEnum.fromValue(queue.getSource());
                final String cardOrderId = queue.getCardOrderId();
                if (queue.getCreateTime().plusMinutes(inQueueTimeoutMinutes).isBefore(LocalDateTime.now())) {
                    log.info("处理将要失败的in队列-获取卡密商品锁-开始, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                            queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination());

                    lockRedisDAO.lockProduct(source, queue.getProductNo(), queue.getBuyDenomination(), () -> {
                        log.info("处理将要失败的in队列-获取卡密商品锁-成功, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                                queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination());

                        inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.FINAL_FAILURE, null);
                        log.warn("处理将要失败的in队列-更新in队列结果-卡密订单最终失败, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                                queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination());
                        return null;
                    });
                } else {
                    log.info("处理将要失败的in队列-卡密订单未超时, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                            queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination());
                }
            });
        }

        log.info("处理将要失败的in队列-完成");
    }

    // 处理未完成的队列
    private void handleUnProcessingInQueue() {
        final String rootTraceId = TraceIdUtil.getTraceId();

        final List<CardKeySourceEnum> sourcetList = Arrays.asList(CardKeySourceEnum.values());
        sourcetList.parallelStream().forEach(source -> {
            if (source == CardKeySourceEnum.MANUAL) {
                // 手动发货没有 inQueue不处理
                return;
            }

            try {
                // 处理source级别的in队列
                handleSourceInQueues(source);
            } catch (Exception e) {
                log.error(StrUtil.format("轮训in队列-按照source-异常, source = {}", source), e);
            }
        });

        TraceIdUtil.setTraceId(rootTraceId);
    }

    // 处理source级别的in队列
    private void handleSourceInQueues(CardKeySourceEnum source) {
        log.info("轮训in队列-按照source-开始, source = {}", source);

        // 是否支持支持聚合订单
        final Boolean supportAggregatedOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getAggregatedOrder();
        log.info("是否支持支持聚合订单, source = {}, supportAggregatedOrder = {}", source, supportAggregatedOrder);

        if (supportAggregatedOrder) {
            // 先分配卡密订单id
            final int row = inQueueService.assignCardOrderId(source);
            log.info("分配卡密订单id, source = {}, row = {}", source, row);
        }

        // 查询该source的未完成的in队列
        final List<CardkeyQueueInDO> sourceQueueList = inQueueService.getCardKeyInQueueByStatus(
                source, Arrays.asList(CardkeyInQueueStatusEnum.INIT, CardkeyInQueueStatusEnum.IN_PROGRESS));
        log.info("查询该source的未完成的in队列, source = {}, sourceQueueListSize = {}", source, sourceQueueList.size());
        if (CollUtil.isEmpty(sourceQueueList)) {
            log.info("轮训in队列-按照source-完成, source = {}, sourceQueueListSize = {}", source, sourceQueueList.size());
            return;
        }

        if (supportAggregatedOrder) {
            // 支持聚合订单，处理聚合订单的 inQueue
            handleAggregatedOrderInQueues(source, sourceQueueList);
        } else {
            // 不支持聚合订单，处理普通的 inQueue
            handleGeneralInQueues(source, sourceQueueList);
        }

        log.info("轮训in队列-按照source-完成, source = {}, sourceQueueListSize = {}", source, sourceQueueList.size());
    }

    // 不支持聚合订单，处理普通的 inQueue
    private void handleGeneralInQueues(CardKeySourceEnum source, List<CardkeyQueueInDO> sourceQueueList) {
        // sourceQueueList按照productNo/denomination分组
        final Map<String, List<CardkeyQueueInDO>> tempQueueMap = sourceQueueList.stream()
                .collect(Collectors.groupingBy(queue -> CardKeySourceEnum.fromValue(queue.getSource()) + "_" + queue.getProductNo() + "_" + queue.getBuyDenomination()));
        final Map<String, List<CardkeyQueueInDO>> productQueueMap = Collections.synchronizedMap(tempQueueMap);
        final Map<String, List<String>> productQueueIdMap = productQueueMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().stream().map(CardkeyQueueInDO::getId).collect(Collectors.toList())
                ));
        log.info("in队列按照商品分组, source = {}, productQueueIdMap = {}", source, JsonUtils.toJsonString(productQueueIdMap));

        final String mainTraceId = TraceIdUtil.getTraceId();
        final Boolean parallelOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getParallelOrder();
        final Stream<String> productQueueMapStream = parallelOrder ? productQueueMap.keySet().parallelStream() : productQueueMap.keySet().stream();

        productQueueMapStream.forEach(productKey -> {
            final List<CardkeyQueueInDO> productQueueList = productQueueMap.getOrDefault(productKey, new ArrayList<>());
            final List<String> productQueueIdList = productQueueList.stream().map(v -> v.getId()).collect(Collectors.toList());

            try {
                TraceIdUtil.setTraceId(IdUtil.fastSimpleUUID());
                log.info("处理商品in队列-开始, productKey = {}, productQueueListSize = {}, productQueueIdList = {}",
                        productKey, productQueueList.size(), JsonUtils.toJsonString(productQueueIdList));

                if (CollUtil.isNotEmpty(productQueueList)) {
                    final String productNo = productQueueList.get(0).getProductNo();
                    final Integer buyDenomination = productQueueList.get(0).getBuyDenomination();
                    final CardkeyProductDO product = cardkeyProductService.getCardKeyProductByNo(productNo);

                    // 是否支持单商品多数量订单
                    final Boolean supportsMultipleQuantitiesOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getMultipleQuantitiesOrder();
                    if (supportsMultipleQuantitiesOrder) {
                        // 支持，productQueueList按照cardKeyOrderId分组
                        final Map<String, List<CardkeyQueueInDO>> cardKeyOrderIdQueueMap = productQueueList.stream()
                                .collect(Collectors.groupingBy(CardkeyQueueInDO::getCardOrderId));
                        final Map<String, List<String>> cardKeyOrderIdQueueIdMap = cardKeyOrderIdQueueMap.entrySet().stream()
                                .collect(Collectors.toMap(
                                        Map.Entry::getKey,
                                        entry -> entry.getValue().stream().map(CardkeyQueueInDO::getId).collect(Collectors.toList())
                                ));
                        log.info("productQueueList按照cardKeyOrderId分组, productNo = {}, buyDenomination = {}, cardKeyOrderIdQueueIdMap = {}",
                                productNo, buyDenomination, JsonUtils.toJsonString(cardKeyOrderIdQueueIdMap));

                        // 创建或查询单商品多数量订单
                        createOrQueryMultipleQuantitiesOrder(source, product, buyDenomination, cardKeyOrderIdQueueMap);
                    } else {
                        // 创建或查询单商品单数量订单
                        createOrQuerySingleQuantitiesOrder(source, product, buyDenomination, productQueueList);
                    }
                }

                log.info("处理商品in队列-完成, productKey = {}, productQueueListSize = {}, productQueueIdList = {}",
                        productKey, productQueueList.size(), JsonUtils.toJsonString(productQueueIdList));
            } catch (Exception e) {
                log.error(StrUtil.format("处理商品in队列-异常, productKey = {}, productQueueListSize = {}, productQueueIdList = {}",
                        productKey, productQueueList.size(), JsonUtils.toJsonString(productQueueIdList)), e);
            } finally {
                TraceIdUtil.removeTraceId();
            }
        });

        TraceIdUtil.setTraceId(mainTraceId);
    }

    // 创建或查询单商品单数量订单
    private void createOrQuerySingleQuantitiesOrder(CardKeySourceEnum source, CardkeyProductDO product,
                                                    Integer buyDenomination, List<CardkeyQueueInDO> productQueues) {
        // 处理每一个订单
        final Boolean parallelOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getParallelOrder();
        final Stream<CardkeyQueueInDO> productQueuesStream = parallelOrder ? productQueues.parallelStream() : productQueues.stream();
        productQueuesStream.forEach(queue -> {
            log.info("创建或查询单商品单数量订单-开始, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                    queue.getId(), source, queue.getCardOrderId(), product.getProductNo(), buyDenomination);

            try {
                // 查询卡密订单
                CdkGenCardResult cardResult = cdkChannelManager.queryOrderResult(queue.getCardOrderId());

                if (cardResult == null) {
                    // 订单不存在

                    // 修改状态为处理中
                    inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.INIT, CardkeyInQueueStatusEnum.IN_PROGRESS, null);
                    log.info("修改inQueue状态为处理中, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                            queue.getId(), source, queue.getCardOrderId(), product.getProductNo(), buyDenomination);

                    // 创建单商品单数量订单
                    final CdkGenCardReq req = createSingleQuantitiesOrderReq(product, buyDenomination, queue);
                    cardResult = cdkChannelManager.createSingleQuantitiesOrder(source, req);
                }

                // 处理卡密订单结果
                handleCardOrderResult(queue.getCardOrderId(), CollUtil.newArrayList(queue), source, cardResult);

                log.info("创建或查询单商品单数量订单-完成, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, inStatus = {}",
                        queue.getId(), source, queue.getCardOrderId(), product.getProductNo(), buyDenomination, queue.getInStatus());
            } catch (Exception e) {
                log.error(StrUtil.format("创建或查询单商品单数量订单-异常, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, inStatus = {}",
                        queue.getId(), source, queue.getCardOrderId(), product.getProductNo(), buyDenomination, queue.getInStatus()), e);
            }
        });
    }

    // 创建单商品单数量订单req
    private static CdkGenCardReq createSingleQuantitiesOrderReq(CardkeyProductDO product, Integer buyDenomination, CardkeyQueueInDO queue) {
        final CdkGenCardReq req = new CdkGenCardReq();
        req.setCardOrderId(queue.getCardOrderId());

        final CdkGenCardReq.Product reqProduct = new CdkGenCardReq.Product();
        reqProduct.setProductNo(product.getProductNo());
        reqProduct.setCurrencyCode(product.getCurrencyCode());
        reqProduct.setBuyDenomination(buyDenomination);
        reqProduct.setQuantity(1);

        req.setProducts(CollUtil.newArrayList(reqProduct));
        return req;
    }

    // 创建或查询单商品多数量订单
    private void createOrQueryMultipleQuantitiesOrder(CardKeySourceEnum source, CardkeyProductDO product,
                                                      Integer buyDenomination, Map<String, List<CardkeyQueueInDO>> cardKeyOrderIdQueueMap) {
        // 处理每一个订单
        final Boolean parallelOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getParallelOrder();
        final Stream<Map.Entry<String, List<CardkeyQueueInDO>>> cardKeyOrderIdQueueMapStream = parallelOrder
                ? cardKeyOrderIdQueueMap.entrySet().parallelStream() : cardKeyOrderIdQueueMap.entrySet().stream();
        cardKeyOrderIdQueueMapStream.forEach(entry -> {
            final String cardOrderId = entry.getKey();
            final List<CardkeyQueueInDO> cardOrderQueues = entry.getValue();
            final List<String> cardOrderQueueIdList = cardOrderQueues.stream().map(v -> v.getId()).collect(Collectors.toList());
            log.info("创建或查询单商品多数量订单-开始, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, cardOrderQueuesSize = {}, cardOrderQueueIdList = {}",
                    source, cardOrderId, product.getProductNo(), buyDenomination, cardOrderQueues.size(), JsonUtils.toJsonString(cardOrderQueueIdList));

            try {
                // 查询卡密订单
                CdkGenCardResult cardResult = cdkChannelManager.queryOrderResult(cardOrderId);

                if (cardResult == null) {
                    // 订单不存在

                    // 修改状态为处理中
                    transactionTemplate.execute(t -> {
                        for (CardkeyQueueInDO queue : cardOrderQueues) {
                            inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.INIT,
                                    CardkeyInQueueStatusEnum.IN_PROGRESS, null);
                        }
                        return null;
                    });
                    log.info("修改inQueue状态为处理中, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, cardOrderQueueIdList = {}",
                            source, cardOrderId, product.getProductNo(), buyDenomination, JsonUtils.toJsonString(cardOrderQueueIdList));

                    // 创建单商品多数量订单
                    final CdkGenCardReq req = createMultipleQuantitiesOrderReq(product, buyDenomination, cardOrderQueues, cardOrderId);
                    cardResult = cdkChannelManager.createMultipleQuantitiesOrder(source, req);
                }

                // 处理卡密订单结果
                handleCardOrderResult(cardOrderId, cardOrderQueues, source, cardResult);

                log.info("创建或查询单商品多数量订单-完成, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, cardOrderQueuesSize = {}, cardOrderQueueIdList = {}",
                        source, cardOrderId, product.getProductNo(), buyDenomination, cardOrderQueues.size(), JsonUtils.toJsonString(cardOrderQueueIdList));
            } catch (Exception e) {
                log.error(StrUtil.format("创建或查询单商品多数量订单-异常, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, cardOrderQueuesSize = {}, cardOrderQueueIdList = {}",
                        source, cardOrderId, product.getProductNo(), buyDenomination, cardOrderQueues.size(), JsonUtils.toJsonString(cardOrderQueueIdList)), e);
            }
        });
    }

    // 创建单商品多数量订单req
    private static CdkGenCardReq createMultipleQuantitiesOrderReq(CardkeyProductDO product, Integer buyDenomination,
                                                                  List<CardkeyQueueInDO> cardOrderQueues, String cardOrderId) {
        final int quantity = cardOrderQueues.size();
        final CdkGenCardReq req = new CdkGenCardReq();
        req.setCardOrderId(cardOrderId);

        final CdkGenCardReq.Product reqProduct = new CdkGenCardReq.Product();
        reqProduct.setProductNo(product.getProductNo());
        reqProduct.setCurrencyCode(product.getCurrencyCode());
        reqProduct.setBuyDenomination(buyDenomination);
        reqProduct.setQuantity(quantity);

        req.setProducts(CollUtil.newArrayList(reqProduct));
        return req;
    }

    // 处理聚合订单的 inQueue
    private void handleAggregatedOrderInQueues(CardKeySourceEnum source, List<CardkeyQueueInDO> sourceQueueList) {
        // sourceQueueList按照卡密订单id分组
        final Map<String, List<CardkeyQueueInDO>> cardOrderQueueMap = sourceQueueList.stream()
                .collect(Collectors.groupingBy(queue -> queue.getCardOrderId()));
        final Map<String, List<String>> cardOrderQueueIdMap = cardOrderQueueMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().stream().map(CardkeyQueueInDO::getId).collect(Collectors.toList())
                ));
        log.info("sourceQueueList按照卡密订单id分组, source = {}, cardOrderQueueIdMap = {}",
                source, JsonUtils.toJsonString(cardOrderQueueIdMap));

        final String mainTraceId = TraceIdUtil.getTraceId();
        final Boolean parallelOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getParallelOrder();
        final Stream<Map.Entry<String, List<CardkeyQueueInDO>>> cardOrderQueueMapStream = parallelOrder
                ? cardOrderQueueMap.entrySet().parallelStream() : cardOrderQueueMap.entrySet().stream();

        cardOrderQueueMapStream.forEach(entry -> {
            final String cardOrderId = entry.getKey();
            final List<CardkeyQueueInDO> cardOrderQueueList = entry.getValue();
            final List<String> cardOrderQueueIdList = cardOrderQueueList.stream().map(v -> v.getId()).collect(Collectors.toList());
            try {
                TraceIdUtil.setTraceId(IdUtil.fastSimpleUUID());

                // 创建或查询聚合订单，整个cardOrder队列一次性下单
                createOrQueryAggregatedCardOrder(source, cardOrderId, cardOrderQueueList);
            } catch (Exception e) {
                log.error(StrUtil.format("创建或查询聚合订单-异常, source = {}, cardOrderId = {}, cardOrderQueueListSize = {}, cardOrderQueueIdList = {}",
                        source, cardOrderId, cardOrderQueueList.size(), JsonUtils.toJsonString(cardOrderQueueIdList)), e);
            } finally {
                TraceIdUtil.removeTraceId();
            }
        });

        TraceIdUtil.setTraceId(mainTraceId);
    }

    // 创建或查询聚合订单，整个cardOrder队列一次性下单
    private void createOrQueryAggregatedCardOrder(CardKeySourceEnum source, String cardOrderId, List<CardkeyQueueInDO> cardOrderQueueList) {
        final List<String> cardOrderQueueIdList = cardOrderQueueList.stream().map(v -> v.getId()).collect(Collectors.toList());
        log.info("创建或查询聚合订单-开始, source = {}, cardOrderId = {}, cardOrderQueueListSize = {}, cardOrderQueueIdList = {}",
                source, cardOrderId, cardOrderQueueList.size(), JsonUtils.toJsonString(cardOrderQueueIdList));

        final List<CdkGenCardReq.Product> products = new ArrayList<>();

        // 查询卡密订单
        CdkGenCardResult cardResult = cdkChannelManager.queryOrderResult(cardOrderId);

        if (cardResult == null) {
            // 卡密订单不存在

            // 修改inQueue状态为处理中
            transactionTemplate.execute(t -> {
                for (CardkeyQueueInDO queue : cardOrderQueueList) {
                    inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.INIT,
                            CardkeyInQueueStatusEnum.IN_PROGRESS, null);
                }
                return null;
            });
            log.info("修改inQueue状态为处理中, source = {}, cardOrderId = {}, cardOrderQueueIdList = {}",
                    source, cardOrderId, JsonUtils.toJsonString(cardOrderQueueIdList));

            // 创建卡密聚合订单
            final CdkGenCardReq req = createAggregatedOrderReq(cardOrderId, cardOrderQueueList, products);
            cardResult = cdkChannelManager.createAggregatedOrder(source, req);
        }

        // 处理卡密订单结果
        handleCardOrderResult(cardOrderId, cardOrderQueueList, source, cardResult);

        log.info("创建或查询聚合订单-完成, source = {}, cardOrderId = {}, cardOrderQueueListSize = {}, cardOrderQueueIdList = {}",
                source, cardOrderId, cardOrderQueueList.size(), JsonUtils.toJsonString(cardOrderQueueIdList));
    }

    // 创建卡密聚合订单req
    private static CdkGenCardReq createAggregatedOrderReq(String cardOrderId, List<CardkeyQueueInDO> cardOrderQueueList,
                                                          List<CdkGenCardReq.Product> products) {
        // 按照产品编号 + 面值分组，将产品编号 + 面值相等的放到同一个 product 对象中
        final Map<String, List<CardkeyQueueInDO>> productQueueMap = cardOrderQueueList.stream()
                .collect(Collectors.groupingBy(queue -> queue.getProductNo() + "_" + queue.getBuyDenomination()));

        for (List<CardkeyQueueInDO> queues : productQueueMap.values()) {
            final String productNo = queues.get(0).getProductNo();
            final String currencyCode = queues.get(0).getCurrencyCode();
            final Integer buyDenomination = queues.get(0).getBuyDenomination();
            final int quantity = queues.size();

            final CdkGenCardReq.Product product = new CdkGenCardReq.Product();
            product.setProductNo(productNo);
            product.setCurrencyCode(currencyCode);
            product.setBuyDenomination(buyDenomination);
            product.setQuantity(quantity);
            products.add(product);
        }

        final CdkGenCardReq req = new CdkGenCardReq();
        req.setCardOrderId(cardOrderId);
        req.setProducts(products);
        return req;
    }

    // 处理卡密订单结果
    private void handleCardOrderResult(String cardOrderId, List<CardkeyQueueInDO> queueList,
                                       CardKeySourceEnum source, CdkGenCardResult cardResult) {
        final List<String> queueIdList = queueList.stream().map(v -> v.getId()).collect(Collectors.toList());
        final CdkOrderStatusEnum orderStatus = cardResult.getOrderStatus();

        // 是否订单完成并且有成功的卡密
        final boolean isCreateCardCompleted = orderStatus.isCreateCardCompletedStatus();
        if (isCreateCardCompleted) {
            // 卡密订单已完成（可能成功或失败）
            log.info("卡密订单已完成, cardOrderId = {}, orderStatus = {}, createDuration = {}, queueIdList = {}, cardResult = {}",
                    cardOrderId, orderStatus, cardResult.getCreateDuration(),
                    JsonUtils.toJsonString(queueIdList), JsonUtils.toJsonString(cardResult));
            handleGenCardCompletedResult(cardOrderId, source, cardResult, new ArrayList(queueList));
        } else {
            log.warn("卡密订单未完成, cardOrderId = {}, orderStatus = {}, createDuration = {}, queueIdList = {}, cardResult = {}",
                    cardOrderId, orderStatus, cardResult.getCreateDuration(),
                    JsonUtils.toJsonString(queueIdList), JsonUtils.toJsonString(cardResult));
            handleGenCardUnCompletedResult(cardOrderId, new ArrayList(queueList), orderStatus);
        }
    }

    // 卡密订单未完成
    private void handleGenCardUnCompletedResult(String cardOrderId, List<CardkeyQueueInDO> queues, CdkOrderStatusEnum orderStatus) {
        final String traceId = TraceIdUtil.getTraceId();

        // 判断是否超时
        final Long inQueueTimeoutMinutes = cardKeyConfig.getGlobal().getInQueueTimeoutMinutes();
        queues.parallelStream().forEach(queue -> {
            try {
                TraceIdUtil.setTraceId(traceId);

                final CardKeySourceEnum source = CardKeySourceEnum.fromValue(queue.getSource());
                if (queue.getCreateTime().plusMinutes(inQueueTimeoutMinutes).isBefore(LocalDateTime.now())) {
                    log.info("卡密订单未完成-获取卡密商品锁-开始, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, orderStatus = {}",
                            queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination(), orderStatus);

                    lockRedisDAO.lockProduct(source, queue.getProductNo(), queue.getBuyDenomination(), () -> {
                        log.info("卡密订单未完成-获取卡密商品锁-成功, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, orderStatus = {}",
                                queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination(), orderStatus);

                        inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.TIMEOUT, null);
                        log.warn("卡密订单未完成-更新in队列结果-卡密订单已超时, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, orderStatus = {}",
                                queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination(), orderStatus);
                        return null;
                    });
                } else {
                    log.info("卡密订单未完成-卡密订单未超时, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, orderStatus = {}",
                            queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination(), orderStatus);
                }
            } finally {
                TraceIdUtil.removeTraceId();
            }
        });

        TraceIdUtil.setTraceId(traceId);
    }

    // 卡密订单已完成
    private void handleGenCardCompletedResult(String cardOrderId, CardKeySourceEnum source,
                                              CdkGenCardResult cardResult, List<CardkeyQueueInDO> queues) {
        // 必须保证在一个事务中将同一个cardOrderId中的所有 queue 一次性处理完毕
        final Set<String> productLockKeys = getProductLockKeys(source, queues);
        log.info("卡密订单已完成-获取卡密商品锁-开始, source = {}, cardOrderId = {}, productLockKeys = {}",
                source, cardOrderId, productLockKeys);

        lockRedisDAO.lockProducts(productLockKeys, () -> {
            log.info("卡密订单已完成-获取卡密商品锁-成功, source = {}, cardOrderId = {}, productLockKeys = {}",
                    source, cardOrderId, productLockKeys);

            // 移出in队列,同时插入卡密
            // 因为在线程池中执行没有被Bean管理，只能手动提交事务
            transactionTemplate.execute(t -> {
                if (CollUtil.isNotEmpty(cardResult.getCards())) {
                    for (CdkCardSecret card : cardResult.getCards()) {
                        // 保存卡密
                        saveCards(cardResult, cardOrderId, source, card);
                    }
                }

                // 更新in队列结果
                updateInQueueStatus(source, cardOrderId, queues, cardResult);

                return null;
            });
            return null;
        });
    }

    private Set<String> getProductLockKeys(CardKeySourceEnum source, List<CardkeyQueueInDO> queues) {
        final Set<String> lockKeys = new HashSet<>();
        for (CardkeyQueueInDO queue : queues) {
            final String productKey = lockRedisDAO.getProductKey(source, queue.getProductNo(), queue.getBuyDenomination());
            lockKeys.add(productKey);
        }
        return lockKeys;
    }

    @Data
    @Builder
    public static class ProductInfo {
        private String productNo;
        private Integer buyDenomination;
    }

    // 更新in队列结果
    private void updateInQueueStatus(CardKeySourceEnum source, String cardOrderId, List<CardkeyQueueInDO> queues, CdkGenCardResult cardResult) {
        // cards按照productNo/buyDenomination分组
        final Map<String, List<CdkCardSecret>> cardMap = cardResult.getCards().stream()
                .collect(Collectors.groupingBy(card -> card.getProductNo() + "_" + card.getBuyDenomination()));

        // queues按照productNo/buyDenomination分组
        final Map<String, List<CardkeyQueueInDO>> queueMap = queues.stream()
                .collect(Collectors.groupingBy(queue -> queue.getProductNo() + "_" + queue.getBuyDenomination()));

        for (Map.Entry<String, List<CardkeyQueueInDO>> entry : queueMap.entrySet()) {
            final String key = entry.getKey();
            final List<CardkeyQueueInDO> productQueues = entry.getValue();
            final List<CdkCardSecret> productCards = cardMap.getOrDefault(key, new ArrayList<>());

            // 按照产品, 更新in队列结果
            updateInQueueStatusByProduct(source, cardOrderId, productQueues, productCards, cardResult.getErrorMsg());
        }
    }

    // 按照产品, 更新in队列结果
    private void updateInQueueStatusByProduct(CardKeySourceEnum source, String cardOrderId, List<CardkeyQueueInDO> productQueues,
                                              List<CdkCardSecret> productCards, String errorMsg) {
        // 对queues按照id正序进行排序
        productQueues.sort(Comparator.comparing(CardkeyQueueInDO::getId));

        final List<String> productQueueIdList = productQueues.stream().map(v -> v.getId()).collect(Collectors.toList());

        if (productQueues.size() == productCards.size()) {
            // 卡密订单全部成功
            log.info("卡密订单已完成-卡密订单全部成功, source = {}, cardOrderId = {}, productQueuesSize = {}, productQueueIdList = {}",
                    source, cardOrderId, productQueues.size(), JsonUtils.toJsonString(productQueueIdList));

            for (CardkeyQueueInDO queue : productQueues) {
                inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.SUCCESS, null);
                log.info("卡密订单已完成-更新in队列结果-卡密订单成功, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                        queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination());
            }
        } else {
            // 卡密订单部分成功
            log.warn("卡密订单已完成-卡密订单部分成功, source = {}, cardOrderId = {}, productQueuesSize = {}, productCardsSize = {}, productQueueIdList = {}",
                    source, cardOrderId, productQueues.size(), productCards.size(), JsonUtils.toJsonString(productQueueIdList));

            for (int i = 0; i < productQueues.size(); i++) {
                final CardkeyQueueInDO queue = productQueues.get(i);
                if (i < productCards.size()) {
                    // 成功
                    inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.SUCCESS, null);
                    log.info("卡密订单已完成-更新in队列结果-卡密订单成功, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}",
                            queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination());
                } else {
                    // 失败
                    inQueueService.updateCardKeyInQueueStatus(queue.getId(), CardkeyInQueueStatusEnum.WILL_FAIL, errorMsg);
                    log.warn("卡密订单已完成-更新in队列结果-卡密订单失败, queueId = {}, source = {}, cardOrderId = {}, productNo = {}, buyDenomination = {}, errorMsg = {}",
                            queue.getId(), source, cardOrderId, queue.getProductNo(), queue.getBuyDenomination(), errorMsg);
                }
            }
        }
    }

    // 保存卡密
    private void saveCards(CdkGenCardResult cardResult, String cardOrderId, CardKeySourceEnum source, CdkCardSecret cdkSecret) {
        final CardkeySecretSaveReqVO createReqVO = new CardkeySecretSaveReqVO();
        createReqVO.setEGiftId(cdkSecret.getEGiftId());
        createReqVO.setSource(source.getValue());
        createReqVO.setProductNo(cdkSecret.getProductNo());
        createReqVO.setClaimStatus(ClaimStatusEnum.NO.getValue());
        createReqVO.setCurrencyCode(cdkSecret.getCurrencyCode());
        createReqVO.setDenomination(cdkSecret.getBuyDenomination());
        createReqVO.setCardNumber(cdkSecret.getCardNumber());
        createReqVO.setCode(cdkSecret.getCode());
        createReqVO.setPin(cdkSecret.getPin());
        createReqVO.setCardOrderId(cardOrderId);
        createReqVO.setChannelOrderId(cardResult.getChannelCardOrderId());
        createReqVO.setChannelInnerOrderId(cardResult.getChannelInnerOrderId());
        final String secretId = cardkeySecretService.createCardkeySecret(createReqVO);
        log.info("保存卡密, secretId = {}, source = {}, productNo = {}, buyDenomination = {}, cardOrderId = {}",
                secretId, source, cdkSecret.getProductNo(), cdkSecret.getBuyDenomination(), cardOrderId);
    }

    @Override
    public void pollUnProcessingOutQueue() {
        try {
            final String rootTraceId = IdUtil.fastSimpleUUID();
            TraceIdUtil.setTraceId(rootTraceId);

            log.info("轮训out队列-开始");
            final List<CardkeyQueueOutDO> queueList = outQueueService.getCardKeyOutQueueByStatus(
                    Arrays.asList(CardkeyOutQueueStatusEnum.INIT, CardkeyOutQueueStatusEnum.IN_PROGRESS));
            log.info("轮训out队列, queueList.size = {}", queueList.size());
            if (CollUtil.isEmpty(queueList)) {
                log.info("轮训out队列-结束");
                return;
            }

            // out队列按照商品分组并排序
            final Map<String, List<CardkeyQueueOutDO>> productQueueMap = getProductQueueMap(queueList);

            // 并发处理每个商品队列
            productQueueMap.entrySet().parallelStream().forEach(entry -> {
                TraceIdUtil.setTraceId(IdUtil.fastSimpleUUID());
                final String productKey = entry.getKey();
                final List<CardkeyQueueOutDO> productQueues = entry.getValue();
                final List<String> productQueueIdList = productQueues.stream().map(v -> v.getId()).collect(Collectors.toList());

                try {
                    if (CollUtil.isNotEmpty(productQueues)) {
                        // 处理商品队列级别的out队列
                        handleProductOutQueues(productKey, productQueues, productQueueIdList);
                    }
                } catch (Exception e) {
                    log.error(StrUtil.format("扣减卡密任务-异常, productKey = {}, productQueuesSize = {}, productQueueIdList = {}",
                            productKey, productQueues.size(), JsonUtils.toJsonString(productQueueIdList)), e);
                } finally {
                    TraceIdUtil.removeTraceId();
                }
            });

            TraceIdUtil.setTraceId(rootTraceId);
            log.info("轮训out队列-结束");
        } catch (Exception e) {
            log.error("轮训out队列-异常", e);
        } finally {
            TraceIdUtil.removeTraceId();
        }
    }

    // out队列按照商品分组并排序
    private static Map<String, List<CardkeyQueueOutDO>> getProductQueueMap(List<CardkeyQueueOutDO> queueList) {
        final Map<String, List<CardkeyQueueOutDO>> tempQueueMap = queueList.stream().collect(Collectors.groupingBy(queue ->
                CardKeySourceEnum.fromValue(queue.getSource()) + "_" + queue.getProductNo() + "_" + queue.getBuyDenomination()));

        // 对每个列表按 queueId 正序排序
        tempQueueMap.values().forEach(list ->
                list.sort(Comparator.comparing(CardkeyQueueOutDO::getId))
        );

        // 防止并发问题
        final Map<String, List<CardkeyQueueOutDO>> productQueueMap = Collections.synchronizedMap(tempQueueMap);

        final Map<String, List<String>> productQueueIdMap = productQueueMap.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().stream().map(CardkeyQueueOutDO::getId).collect(Collectors.toList())
                ));
        log.info("out队列按照商品分组, productQueueIdMap = {}", JsonUtils.toJsonString(productQueueIdMap));
        return productQueueMap;
    }

    // 处理商品队列级别的out队列
    private void handleProductOutQueues(String productKey, List<CardkeyQueueOutDO> productQueues, List<String> productQueueIdList) {
        log.info("扣减卡密任务-开始, productKey = {}, productQueuesSize = {}, productQueueIdList = {}",
                productKey, productQueues.size(), JsonUtils.toJsonString(productQueueIdList));

        // productQueue按照id正序进行排序
        productQueues.sort(Comparator.comparing(CardkeyQueueOutDO::getId));

        // 处理每一个队列，注意此处不能 try catch，必须保证是串行的，前一个失败，后续不能再执行
        for (CardkeyQueueOutDO queue : productQueues) {
            // 扣减卡密
            final Collection<String> cardSecretIds = deductCardSecret(queue);
            if (CollUtil.isEmpty(cardSecretIds)) {
                // 扣减失败（超时），不能往下处理，得卡着此队列
                break;
            }
        }

        log.info("扣减卡密任务-完成, productKey = {}, productQueuesSize = {}, productQueueIdList = {}",
                productKey, productQueues.size(), JsonUtils.toJsonString(productQueueIdList));
    }

    // 扣减卡密
    private Collection<String> deductCardSecret(CardkeyQueueOutDO queue) {
        final CardKeySourceEnum source = CardKeySourceEnum.fromValue(queue.getSource());
        final String productNo = queue.getProductNo();
        final Long userId = queue.getUserId();
        final String orderNo = queue.getOrderNo();
        final Integer requiredStock = queue.getRequiredStock();
        final Integer buyDenomination = queue.getBuyDenomination();

        log.info("扣减卡密-获取卡密订单锁-开始, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}",
                queue.getId(), userId, orderNo, source, productNo, buyDenomination);
        return lockRedisDAO.lockOrder(orderNo, () -> {
            log.info("扣减卡密-获取卡密订单锁-成功, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}",
                    queue.getId(), userId, orderNo, source, productNo, buyDenomination);

            // 查询订单是否已发货
            final boolean hasDelivered = hasDeliveredOrder(orderNo);

            if (hasDelivered) {
                // 已经发货，查询卡密
                final List<String> cardSecretIds = getCardSecretIds(orderNo);
                log.warn("扣减卡密-订单已发货, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}, requiredStock = {}, cardSecretIds = {}",
                        queue.getId(), userId, orderNo, source, productNo, buyDenomination, requiredStock, cardSecretIds);

                // 更新out队列为成功
                outQueueService.updateCardKeyOutQueueStatus(queue.getId(), CardkeyOutQueueStatusEnum.SUCCESS, null);
                log.info("更新out队列为成功, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}, requiredStock = {}",
                        queue.getId(), queue.getUserId(), queue.getOrderNo(), source, queue.getProductNo(), queue.getBuyDenomination(), queue.getRequiredStock());
                return cardSecretIds;
            } else {
                log.info("扣减卡密-获取卡密商品锁-开始, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}",
                        queue.getId(), userId, orderNo, source, productNo, buyDenomination);

                return lockRedisDAO.lockProduct(source, productNo, buyDenomination, () -> {
                    log.info("扣减卡密-获取卡密商品锁-成功, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}",
                            queue.getId(), userId, orderNo, source, productNo, buyDenomination);

                    // 重新查询当前可用的卡密
                    final List<CardkeySecretDO> availableSecretList = cardkeySecretService.getCardKeySecretByProductNoAndClaimStatus(
                            ClaimStatusEnum.NO, productNo, buyDenomination, requiredStock);

                    // 计算缺少的卡密
                    final int missingSecretSize = requiredStock - availableSecretList.size();

                    if (missingSecretSize > 0) {
                        // 库存不足
                        log.warn("扣减卡密-库存不足, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}, availableSecretSize = {}, requiredStock = {}, missingSecretSize = {}",
                                queue.getId(), userId, orderNo, source, productNo, buyDenomination, availableSecretList.size(), requiredStock, missingSecretSize);

                        // 判断queue是否超时
                        final Integer outQueueTimeoutMinutes = cardKeyConfig.getChannelDifference().get(source.getKey()).getOutQueueTimeoutMinutes();
                        if (queue.getOutStatusUpdateTime().isBefore(LocalDateTime.now().minusMinutes(outQueueTimeoutMinutes))) {
                            // 扣减卡密超时
                            transactionTemplate.execute(t -> {
                                // 更新out队列为超时
                                outQueueService.updateCardKeyOutQueueStatusTimeout(queue.getId(), null);
                                log.info("更新out队列为超时, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {},",
                                        queue.getId(), queue.getUserId(), queue.getOrderNo(), source, queue.getProductNo(), queue.getBuyDenomination());

                                // 更新订单发货状态
                                this.updateOrderDeliveryStatus(queue.getOrderNo(), false, "库存不足（扣减卡密超时）", null);

                                return null;
                            });
                        } else {
                            // 未超时
                        }
                        return null;
                    } else {
                        // 库存充足，扣减密钥，并更新订单发货状态
                        return deductCardSecretSuccess(queue, availableSecretList);
                    }
                });
            }
        });
    }


    // 扣减密钥，并更新订单发货状态
    private Collection<String> deductCardSecretSuccess(CardkeyQueueOutDO queue, List<CardkeySecretDO> availableSecretList) {
        final CardKeySourceEnum source = CardKeySourceEnum.fromValue(queue.getSource());
        return transactionTemplate.execute(t -> {
            // 更新out队列为成功
            outQueueService.updateCardKeyOutQueueStatus(queue.getId(), CardkeyOutQueueStatusEnum.SUCCESS, null);
            log.info("更新out队列为成功, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}, requiredStock = {}",
                    queue.getId(), queue.getUserId(), queue.getOrderNo(), source, queue.getProductNo(), queue.getBuyDenomination(), queue.getRequiredStock());

            // 扣减密钥
            final List<String> cardSecretIds = availableSecretList.stream().map(v -> v.getId()).collect(Collectors.toList());
            cardkeySecretService.deductionSecret(queue.getUserId(), queue.getOrderNo(), cardSecretIds);
            log.info("扣减卡密-成功, queueId = {}, userId = {}, orderNo = {}, source = {}, productNo = {}, buyDenomination = {}, requiredStock = {}, cardSecretIds = {}",
                    queue.getId(), queue.getUserId(), queue.getOrderNo(), source, queue.getProductNo(), queue.getBuyDenomination(), queue.getRequiredStock(), cardSecretIds);

            // 更新订单发货状态
            this.updateOrderDeliveryStatus(queue.getOrderNo(), true, null, availableSecretList);

            return cardSecretIds;
        });
    }

    // 更新订单发货状态
    private void updateOrderDeliveryStatus(String orderNo, boolean success, String reason, List<CardkeySecretDO> availableSecretList) {
        if (reason != null && reason.startsWith("java.lang.RuntimeException: ")) {
            reason = reason.replace("java.lang.RuntimeException: ", "");
        }
        if (success) {
            log.info("更新订单发货状态为成功, orderNo = {}, reason = {}", orderNo, reason);
        } else {
            log.info("更新订单发货状态为失败, orderNo = {}, reason = {}", orderNo, reason);
        }

        String cdkChannelOrderId = null;
        String cdkChannelInnerOrderId = null;
        if (CollUtil.isNotEmpty(availableSecretList)) {
            cdkChannelOrderId = String.join(",", availableSecretList.stream().map(v -> v.getChannelOrderId()).collect(Collectors.toSet()));
            cdkChannelInnerOrderId = String.join(",", availableSecretList.stream().map(v -> v.getChannelInnerOrderId()).collect(Collectors.toSet()));
        } else {
            log.warn("卡密信息不存在, orderNo = {}", orderNo);
        }
        orderApi.deliveryOrderByNo(orderNo, cdkChannelOrderId, cdkChannelInnerOrderId, success, reason);
    }

    // 查询卡密
    private List<String> getCardSecretIds(String orderNo) {
        final List<CardkeySecretDO> cardSecretList = cardkeySecretService.getCardKeySecretByOrderNo(orderNo);
        final List<String> cardSecretIds = cardSecretList.stream().map(v -> v.getId()).collect(Collectors.toList());
        return cardSecretIds;
    }

    // 查询订单是否已发货
    private boolean hasDeliveredOrder(String orderNo) {
        // 查询订单发货状态
        final TradeOrderRespDTO order = orderApi.getOrderByNo(orderNo);
        log.info("扣减卡密-查询订单发货状态, orderNo = {}, deliveryStatus = {}", orderNo, order.getDeliveryStatus());

        final boolean hasDelivered = TradeOrderDeliveryStatusEnum.hasDelivered(order.getDeliveryStatus());
        return hasDelivered;
    }

    // 获取面额
    private static Integer getBuyDenomination(Long userId, String orderNo, Integer orderDenomination,
                                              CardkeyProductDO cardKeyProduct, ProductSkuRespDTO sku) {
        final Integer buyDenomination;
        final Boolean customDenomination = cardKeyProduct.getCustomDenomination();
        final Integer denominationFixed = cardKeyProduct.getDenominationFixed();

        if (customDenomination) {
            // product自定义面额
            if (sku.getCustomDenomination()) {
                // sku自定义面额，使用用户传入的
                buyDenomination = orderDenomination;
            } else {
                // sku固定面额
                buyDenomination = sku.getDenominationFixed();
            }
        } else {
            // product固定面额，使用商品中定义好的
            buyDenomination = cardKeyProduct.getDenominationFixed();
        }

        if (buyDenomination == null) {
            log.error("面额为空, userId = {}, orderNo = {}, productNo = {}, customDenomination = {}, productDenomination = {}, orderDenomination = {}",
                    userId, orderNo, cardKeyProduct.getProductNo(), customDenomination, denominationFixed, orderDenomination);
            throw new RuntimeException(String.format("领取卡密失败-面额为空, customDenomination = %s, productDenomination = %s, orderDenomination = %s",
                    customDenomination, denominationFixed, orderDenomination));
        }

        log.info("获取面额, productNo = {}, buyDenomination = {}", cardKeyProduct.getProductNo(), buyDenomination);
        return buyDenomination;
    }

    @Override
    public void synAllProducts(CardKeySourceEnum source) {
        final Boolean enableSynCardKeyProduct = cardKeyConfig.getEnableSynCardKeyProduct();
        log.info("获取卡密商品-开始, enableSynCardKeyProduct = {}", enableSynCardKeyProduct);
        if (!enableSynCardKeyProduct) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CARDKEY_DEFAULT_ERROR, "未打开同步卡密商品");
        }

        final List<CdkProduct> products = cdkChannelManager.getAllProducts(source);
        for (CdkProduct product : products) {
            if (product == null) {
                continue;
            }
            final String productNo = product.getProductNo();
            CardkeyProductDO productDO = cardkeyProductService.getCardKeyProductByNo(productNo);
            final boolean insert = productDO == null;
            if (insert) {
                productDO = BeanUtils.toBean(product, CardkeyProductDO.class);
                cardkeyProductService.createCardkeyProduct(productDO);
            } else {
                // TODO: 待完善
                log.warn("获取卡密商品-已经存在, productNo = {}", productNo);
//                    cardkeyProductService.updateCardkeyProduct(productDO);
            }
        }

        log.info("获取卡密商品-完成");
    }

}
